﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;

namespace CodeAudit
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class CodeAuditAnalyzer : DiagnosticAnalyzer
    {
        public const string DiagnosticId = "CodeAudit";

        // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
        // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization
        private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources));
        private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
        private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources));
        private const string Category = "Naming";

        private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

        public override void Initialize(AnalysisContext context)
        {
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
            context.EnableConcurrentExecution();

            // TODO: Consider registering other actions that act on syntax instead of or in addition to symbols
            // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
            context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.LocalDeclarationStatement);
        }

        private void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
            // 检查任何 const 修饰符的声明。 一旦找到，请立即返回.确保声明不是const:
            if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
            {
                return;
            }
            // 对局部声明执行数据流分析。
            DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);

            // 检索局部声明中每个变量的局部符号
            // 并使用context.SemanticModel分析的结果确保局部变量不会在任何其他位置用新值来编写。
            VariableDeclaratorSyntax variable = localDeclaration.Declaration.Variables.Single();
            string varName = variable.Identifier.Text;
            if (IsSmallHump(varName))
            {
                return;
            }
            context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), localDeclaration.Declaration.Variables.First().Identifier.ValueText));
        }
        /// <summary>
        /// 判断变量名称是否符合小驼峰
        /// </summary>
        /// <param name="varName">变量名</param>
        /// <returns></returns>
        public bool IsSmallHump(string varName)
        {
            if (string.IsNullOrWhiteSpace(varName))
            {
                // 空字符串或只包含空白字符的字符串不符合命名规则  
                return false;
            }

            // 第一个字符必须是小写字母  
            if (!char.IsLower(varName[0]))
            {
                return false;
            }

            // 后续字符可以是大写字母、小写字母或数字（但不允许以下划线开头）  
            for (int i = 1; i < varName.Length; i++)
            {
                char currentChar = varName[i];
                if (!char.IsLetterOrDigit(currentChar) && currentChar != '_')
                {
                    // 如果遇到非字母、非数字且不是下划线的字符，则不符合命名规则  
                    return false;
                }

                // 如果当前字符是大写字母，且前一个字符也是大写字母（或者不存在），则不符合规则（因为应该有小写字母分隔单词）  
                if (char.IsUpper(currentChar) && (i > 1 && char.IsUpper(varName[i - 1])))
                {
                    return false;
                }
            }

            // 如果所有检查都通过，那么字符串符合小驼峰命名规则  
            return true;
        }
    }
}
